﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Threading;
using System.Collections;
using Nollie.Utilities;

namespace Nollie.Storage.Memory
{
	/// <summary>
	/// This collection is special as it supports retrieval of items through both key and index.
	/// When an item is retrieved from the collection, the item is moved to the beginning of the list.
	/// When an item is added, unless enough capacity, the oldest item is removed.
	/// </summary>
	public class MemoryStorage : KeyIndexCollection
	{
		private int _capacity;
		private ReaderWriterLockSlim _lock;

		public MemoryStorage(int capacitiy) : base(capacitiy)
		{
			_capacity = capacitiy;
			_lock = new ReaderWriterLockSlim();
		}
		public CacheItem Get(string key)
		{
			using (var lockHelper = new WriterLockHelper(_lock))
			{ 
				var cacheItem = (CacheItem) base.BaseGet(key);

				if (cacheItem != null)
				{
					base.BaseRemove(key);
					base.BaseAdd(key, cacheItem);
				}
			
				return (CacheItem) cacheItem;
			}
		}
		public CacheItem GetByIndex(int index)
		{
			using (var lockHelper = new ReaderLockHelper(_lock))
			{
				return (CacheItem) base.BaseGet(index);
			}
		}
		public void Add(CacheItem item)
		{
			using (var lockHelper = new WriterLockHelper(_lock))
			{
				if (base.Count == _capacity)
					base.BaseRemoveAt(0);

				if (base.BaseGet(item.Key) != null)
					base.BaseRemove(item.Key);

				base.BaseAdd(item.Key, item);
			}
		}
		public void RemoveByCacheKey(string key)
		{
			using (var lockHelper = new WriterLockHelper(_lock))
			{
				base.BaseRemove(key);
			}
		}
		public void RemoveByTag(string key)
		{
			Collection<string> keysToRemove = null;

			using (var lockHelper = new ReaderLockHelper(_lock))
			{
				keysToRemove = GetCacheKeysByTag(key);
			}

			using (var lockHelper = new WriterLockHelper(_lock))
			{
				foreach (var cacheKey in keysToRemove)
					base.BaseRemove(cacheKey);
			}
		}
		private Collection<string> GetCacheKeysByTag(string tag)
		{
			CacheItem cache = null;

			var keysToRemove = new Collection<string>();

			foreach (CacheItem item in this)
			{
				cache = (CacheItem) base.BaseGet(item.Key);

				if (cache.Tags.Contains(tag))
					keysToRemove.Add(item.Key);
			}

			return keysToRemove;
		}
		public void Clear()
		{
			using (var lockHelper = new WriterLockHelper(_lock))
			{
				base.BaseClear();
			}
		}
		public new int Count()
		{
			return base.Count;
		}
	}
}
